package com.guli.mall.pms.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guli.common.constant.ProductEnum;
import com.guli.common.pojo.vo.StatusCodeEnum;
import com.guli.common.to.SkuHasStockTo;
import com.guli.common.to.SpuAndSkuListTo;
import com.guli.common.to.SpuAttrTo;
import com.guli.common.to.SpuEsTo;
import com.guli.common.utils.CopyPageProperties;
import com.guli.common.utils.R;
import com.guli.mall.pms.entity.*;
import com.guli.mall.pms.entity.SpuInfoDescEntity;
import com.guli.mall.pms.entity.vo.SpuInfoImgVo;
import com.guli.mall.pms.entity.vo.SpuInfoWithNameVo;
import com.guli.mall.pms.entity.vo.productSpuAdd.*;
import com.guli.mall.pms.feign.*;
import com.guli.mall.pms.service.*;
import com.guli.mall.serviceBase.globalException.MallException;
import com.guli.mall.sms.entity.MemberPriceEntity;
import com.guli.mall.sms.entity.SkuFullReductionEntity;
import com.guli.mall.sms.entity.SkuLadderEntity;
import com.guli.mall.sms.entity.SpuBoundsEntity;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guli.common.utils.PageUtils;

import com.guli.mall.pms.mapper.SpuInfoMapper;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;


@Service("spuInfoService")
@Slf4j
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoMapper, SpuInfoEntity> implements SpuInfoService {

    @Resource
    SpuImagesService spuImagesService;

    @Resource
    SpuInfoDescService spuInfoDescService;

    @Resource
    ProductAttrValueService productAttrValueService;

    @Resource
    SkuInfoService skuInfoService;

    @Resource
    SkuSaleAttrValueService skuSaleAttrValueService;

    @Resource
    CouponServiceFeignClient couponServiceFeignClient;

    @Resource
    SkuImagesService skuImagesService;

    @Resource
    BrandService brandService;

    @Resource
    AttrService attrService;

    @Resource
    ProductCategoryFeignClient productCategoryFeignClient;

    @Resource
    WareInfoServiceFeignClient wareInfoServiceFeignClient;

    @Resource
    SearchServiceFeignClient searchServiceFeignClient;

    @Resource
    AliOssFeignClient aliOssFeignClient;

    @Resource
    AliOssServiceImpl aliOssService;

    private static String ALI_OSS_IMG_PREFIX;

    @Override
    public PageUtils queryPage(Integer currentPage, Integer size, Long spuId) {
        Page<SpuInfoEntity> page = new Page<>(currentPage, size);
        Page<SpuInfoEntity> resultPage = this.page(page);

        if (!ObjectUtils.isEmpty(spuId)) {
            SpuInfoEntity byId = this.getById(spuId);

            List<SpuInfoEntity> collect = resultPage.getRecords().stream().filter(spu -> {
                return spu.getId() != spuId;
            }).collect(Collectors.toList());

            resultPage.setRecords(collect);

            resultPage.getRecords().add(byId);
        }

        return new PageUtils(resultPage);
    }

    @Transactional
    @Override
    public void saveInfoDescAndImg(SpuInfoImgVo spuInfoImgVo) {
        this.save(spuInfoImgVo);

        Long spuId = spuInfoImgVo.getId();

        /*spuInfoImgVo.getImages().stream().map(img->{
            img.setSpuId(spuId);
            return null;
        });*/
        for (SpuImagesEntity image : spuInfoImgVo.getImages()) {
            image.setSpuId(spuId);
        }
        this.spuImagesService.saveBatch(spuInfoImgVo.getImages());

        SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();

        // 将已经插入的spuInfo的id赋值给spuInfoDescirpt作为id
        spuInfoImgVo.getSpuInfoDescEntity().setSpuId(spuId);

        this.spuInfoDescService.insertAndId(spuInfoImgVo.getSpuInfoDescEntity());
    }

    @Override
    public SpuInfoImgVo getSpuInfoImgById(Long spuInfoId) {

        SpuInfoEntity spuInfo = this.getById(spuInfoId);

        QueryWrapper<SpuImagesEntity> wrapper = new QueryWrapper<>();

        List<SpuImagesEntity> list = spuImagesService.list(wrapper);

        SpuInfoDescEntity desc = spuInfoDescService.getById(spuInfoId);

        SpuInfoImgVo spuInfoImgVo = new SpuInfoImgVo();

        BeanUtils.copyProperties(spuInfo, spuInfoImgVo);

        spuInfoImgVo.setImages(list);

        spuInfoImgVo.setSpuInfoDescEntity(desc);

        return spuInfoImgVo;
    }

    @GlobalTransactional // 低并发，适合使用二阶段全局事务控制(AT)
    @Transactional
    @Override
    public void spuAdd(JsonRootBean jsonRootBean) {

        SpuInfoImgVo spuInfo = jsonRootBean.getSpuInfo();

        /**
         * 保存到 spu-info
         * */
        this.save(spuInfo);


        SpuBoundsEntity spuBoundsEntity = new SpuBoundsEntity();

        spuBoundsEntity.setSpuId(spuInfo.getId());

        BeanUtils.copyProperties(spuInfo, spuBoundsEntity);

        /**
         * 调用微服务保存积分 sms_spu_bounds
         * */
        couponServiceFeignClient.saveSpuBounds(spuBoundsEntity);


        List<SpuImagesEntity> images = spuInfo.getImages();

        for (SpuImagesEntity image : images) {
            image.setSpuId(spuInfo.getId());

        }
        /**
         * 保存到 spu-images
         * */
        spuImagesService.saveBatch(images);

        SpuInfoDescEntity spuInfoDescEntity = spuInfo.getSpuInfoDescEntity();

        spuInfoDescEntity.setSpuId(spuInfo.getId());

        /**
         * 保存到 spu-info-desc
         * */
        spuInfoDescService.insertAndId(spuInfoDescEntity);

        /**
         * 获取 product-attr-value
         * */
        List<ProductAttrValueEntity> productAttrValueEntities = jsonRootBean.getSpuAttr();

        for (ProductAttrValueEntity productAttrValueEntity : productAttrValueEntities) {
            // 设置spuInfoId
            productAttrValueEntity.setSpuId(spuInfo.getId());
        }

        // 保存 product-attr-value
        productAttrValueService.saveBatch(productAttrValueEntities);


        /**
         * 保存sku相关属性
         * */
        List<SpuSellAttr> spuSellAttr = jsonRootBean.getSpuSellAttr();

        /**
         * 获取单个总的 sku 记录
         * */
        for (SpuSellAttr sellAttr : spuSellAttr) {


            SkuVo skuVo = sellAttr.getSkuVo();

            // 获取当前skuInfo（主要）
            SkuInfoEntity skuInfo = skuVo.getSkuInfo();

            skuInfo.setBrandId(spuInfo.getBrandId());

            skuInfo.setCatalogId(spuInfo.getCatalogId());

            skuInfo.setSpuId(spuInfo.getId());

            skuInfoService.save(skuInfo);

            /**
             * 保存到 sku-images
             * 因为前端上传来的所有图片是以逗号分隔的形式将URL集合拼接成字符串封装进 sku_desc 字段中
             * 这里作处理，将其保存到 sku-images 表中
             * */
            this.saveToSkuImages(skuInfo.getSkuId(), skuInfo.getSkuDesc(), skuInfo.getSkuDefaultImg());

            /**
             * 获取 当前sku中的sku销售属性
             * */
            List<SkuSaleAttrValueEntity> sellAttrArray = sellAttr.getSellAttrArray();

            for (SkuSaleAttrValueEntity skuSaleAttrValueEntity : sellAttrArray) {

                skuSaleAttrValueEntity.setSkuId(skuInfo.getSkuId());
            }

            skuSaleAttrValueService.saveBatch(sellAttrArray);

            // 获取满减对象
            SkuFullReductionEntity fullReduction = skuVo.getFullReduction();

            fullReduction.setSkuId(skuInfo.getSkuId());

            // 通过微服务调用保存满减信息
            couponServiceFeignClient.saveSkuFullReduction(fullReduction);

            // 保存折扣
            SkuLadderEntity skuLadderEntity = skuVo.getSkuLadder();

            skuLadderEntity.setSkuId(skuInfo.getSkuId());

            couponServiceFeignClient.saveSkuLadder(skuLadderEntity);

            // 该sku的不同会员价格
            List<MemberPriceEntity> memberPriceList = skuVo.getMemberPrice();

            // 设置会员价格的skuId
            for (MemberPriceEntity memberPriceEntity : memberPriceList) {
                memberPriceEntity.setSkuId(skuInfo.getSkuId());
            }

            couponServiceFeignClient.saveBatchMemberPrice(memberPriceList);
        }

    }

    @Override
    public PageUtils<SpuInfoWithNameVo> searchSpuByCondition(SpuInfoEntity spuInfoEntity, Long currentIndex, Integer size) {

        QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();

        if (!ObjectUtils.isEmpty(spuInfoEntity.getCatalogId())) {
            wrapper.eq("catalog_id", spuInfoEntity.getCatalogId());
        }
        if (!ObjectUtils.isEmpty(spuInfoEntity.getBrandId())) {
            wrapper.eq("brand_id", spuInfoEntity.getBrandId());
        }
        if (!StringUtils.isEmpty(spuInfoEntity.getSpuName())) {
            wrapper.like("spu_name", spuInfoEntity.getSpuName());
        }
        if (!ObjectUtils.isEmpty(spuInfoEntity.getPublishStatus())) {
            wrapper.eq("publish_status", spuInfoEntity.getPublishStatus());
        }


        Page<SpuInfoEntity> spuInfoEntityPage = new Page<>(currentIndex, size);

        Page<SpuInfoEntity> page = this.page(spuInfoEntityPage, wrapper);

        Page<SpuInfoWithNameVo> copy = CopyPageProperties.copy(page, SpuInfoWithNameVo.class);

        /**
         * 填充数据
         * */
        for (SpuInfoWithNameVo record : copy.getRecords()) {
            BrandEntity brandEntity = brandService.getById(record.getBrandId());
            record.setBrandName(brandEntity.getName());

            String categoryName = productCategoryFeignClient.getCategoryName(record.getCatalogId());

            record.setCategoryName(categoryName);
        }

        return new PageUtils<>(copy);
    }

    @Transactional
    @Override
    public void updateByIdAndWriteToEs(SpuInfoEntity spuInfo) {
        updateById(spuInfo);

        if (!ObjectUtils.isEmpty(spuInfo.getPublishStatus())) {

            if (spuInfo.getPublishStatus() == ProductEnum.PublishStatus.UP.getStatus()) {
                // 执行上架操作
                try {
                    upItem(spuInfo.getId());
                } catch (Exception e) {
                    throw new MallException(e.getMessage());
                }
            }


        }

    }

    @Override
    public void upItem(Long spuId) throws ExecutionException, InterruptedException {

        SpuInfoEntity spuInfoEntity = this.getById(spuId);

        BrandEntity brandEntity = brandService.getById(spuInfoEntity.getBrandId());

        // 获取该spu对应的category Name
        String categoryName = productCategoryFeignClient.getCategoryName(spuInfoEntity.getCatalogId());

        // 根据 spuId 获取到 其对应的全部 sku
        List<SkuInfoEntity> skuInfoList = skuInfoService.getListBySpuId(spuInfoEntity.getId());

        /**
         * 尝试多线程
         * */
        CompletableFuture<Map<Long, Boolean>> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {

            R r = wareInfoServiceFeignClient.judgeStockExist(skuInfoList.stream().map(spu -> {
                return spu.getSkuId();
            }).collect(Collectors.toList()));

            if (r.getCode() != StatusCodeEnum.SUCCESS.getCode()) {
                throw new MallException(StatusCodeEnum.INKOVE_SERVICE_EXCEPTION.getCode(), "库存微服务返回错误代码：" + r.getCode());
            }

            List<SkuHasStockTo> skuHasStockTos = r.getData(new TypeReference<List<SkuHasStockTo>>() {
            });

            Map<Long, Boolean> skuHasStockMap = skuHasStockTos.stream().collect(Collectors.toMap(SkuHasStockTo::getSkuId, SkuHasStockTo::getHasStock));

            return skuHasStockMap;
        });


        // 获取到 categoryId 下对应的所有属性，且是可以搜索的属性
        List<AttrEntity> allAttr = attrService.getAttrByCategoryIdAndFilterNonSearchType(spuInfoEntity.getCatalogId());

        // 从所有属性对象中提取出 attrIdList
        List<Long> attrIdList = allAttr.stream().map(attr -> {
            return attr.getAttrId();
        }).collect(Collectors.toList());

        // 根据 刚刚获取到的 attrId 和 spuId 联合获取 实际的 key-value
        List<ProductAttrValueEntity> attrList = productAttrValueService.getAttrValueBySpuIdAndAttrIdList(spuInfoEntity.getId(), attrIdList);

        // 将 attrvalueList 拷贝到 to 中
        List<SpuAttrTo> attrValueList = attrList.stream().map(attr -> {
            SpuAttrTo spuAttrTo = new SpuAttrTo();

            BeanUtils.copyProperties(attr, spuAttrTo);

            return spuAttrTo;
        }).collect(Collectors.toList());

        /*R ossHost = aliOssFeignClient.getOssHost();

        String hostAddrPrefix = ossHost.get("host").toString() + "/";*/

        String hostAddrPrefix = aliOssService.getAliOssImgPrefix();

        List<SpuEsTo> spuEsList = skuInfoList.stream().map(sku -> {


            SpuEsTo spuEsTo = new SpuEsTo();

            BeanUtils.copyProperties(sku, spuEsTo);

            // 属性名不一致，要手动拷贝
            spuEsTo.setSkuPrice(sku.getPrice());

            if (!ObjectUtils.isEmpty(sku.getSkuDefaultImg())) {
                if (sku.getSkuDefaultImg().startsWith("20")) {

                    spuEsTo.setSkuImg(hostAddrPrefix + sku.getSkuDefaultImg());
                } else {
                    spuEsTo.setSkuImg(sku.getSkuDefaultImg());
                }
            }


            spuEsTo.setCatelogId(sku.getCatalogId());

            /*
             * 设置品牌名称和品牌默认图片
             * */

            if (brandEntity.getLogo().startsWith("20")) {

                spuEsTo.setBrandImg(hostAddrPrefix + brandEntity.getLogo());
            } else {
                spuEsTo.setBrandImg(brandEntity.getLogo());
            }

            spuEsTo.setBrandName(brandEntity.getName());

            /**
             * 设置属性列表
             * */
            spuEsTo.setAttrs(attrValueList);

            // 设置分类名称
            spuEsTo.setCatelogName(categoryName);

            /**
             * 设置库存 这样每个sku都要查询一次库存，访问过于频繁
             *
             Map<String, String> wareSkuBySkuId = wareInfoServiceFeignClient.getWareSkuBySkuId(sku.getSkuId());

             if (!ObjectUtils.isEmpty(wareSkuBySkuId) && !ObjectUtils.isEmpty(wareSkuBySkuId.get("stock"))) {

             int stock = Integer.parseInt(wareSkuBySkuId.get("stock"));
             spuEsTo.setHasStock(stock > 0);
             }
             */


            return spuEsTo;
        }).collect(Collectors.toList());


        spuEsList.forEach(System.out::println);

        integerCompletableFuture.whenCompleteAsync((Map<Long, Boolean> result, Throwable t) -> {

            log.info("已经查询到库存信息");

            spuEsList.stream().forEach(spuEsTo -> {

                Boolean has = result.get(spuEsTo.getSkuId());

                if (!ObjectUtils.isEmpty(has)) {
                    spuEsTo.setHasStock(has);
                } else {
                    spuEsTo.setHasStock(false);
                }
            });


        }).exceptionally((Throwable t) -> {
            t.printStackTrace();
            log.error("新线程调用ware微服务查询sku是否有库存失败：{}" + t.getMessage());
            //throw new MallException(t.getMessage() + "\t新线程调用ware微服务查询sku是否有库存失败");
            // 不抛出异常
            return null;
        });

        // 设置默认的都是有货
        if (ObjectUtils.isEmpty(integerCompletableFuture.get())) { // 阻塞主线程

            spuEsList.forEach(spuEsTo -> spuEsTo.setHasStock(true));

        }

        // 保存上架数据到 elastic search 的 product 索引下
        R r = searchServiceFeignClient.saveToProduct(spuEsList);

        if (Integer.parseInt(r.get("code").toString()) != StatusCodeEnum.SUCCESS.getCode()) {
            //TODO 重复调用，接口幂等性，重试机制

            TypeReference<Map<String, String>> mapTypeReference = new TypeReference<Map<String, String>>() {
            };

            Map<String, String> data = r.getData(mapTypeReference);

            List<String> errorLIst = data.entrySet().stream().map((key) -> {
                return key.getKey() + "\t" + key.getValue();
            }).collect(Collectors.toList());

            throw new MallException(StatusCodeEnum.INKOVE_SERVICE_EXCEPTION.getCode(), errorLIst.toString());
        }


    }

    @Override
    public Map<Long, SpuAndSkuListTo> getSpuAndSkuBySpuId(List<Long> spuIds) {

        List<SpuInfoEntity> spuInfoEntities = this.getBaseMapper().selectBatchIds(spuIds);

        return spuInfoEntities.parallelStream().map(spu -> {
            List<SkuInfoEntity> skuInfoEntities = skuInfoService.getListBySpuId(spu.getId());

            SpuAndSkuListTo to = new SpuAndSkuListTo();

            BeanUtils.copyProperties(spu, to);

            List<com.guli.common.pojo.vo.SkuVo> arrayList = new ArrayList<>(skuInfoEntities.size());

            for (SkuInfoEntity skuInfoEntity : skuInfoEntities) {

                com.guli.common.pojo.vo.SkuVo skuVo = new com.guli.common.pojo.vo.SkuVo();

                BeanUtils.copyProperties(skuInfoEntity, skuVo);

                arrayList.add(skuVo);
            }

            to.setSkuVos(arrayList);

            return to;
        }).collect(Collectors.toMap(to -> {
            return to.getId();
        }, to -> {
            return to;
        }));


    }


    private void saveToSkuImages(Long skuId, String skuDesc, String defaultImgUrl) {

        if (!StringUtils.isEmpty(skuDesc)) {
            String[] imagesUrlArray = skuDesc.split(",");

            List<String> imagesUrlList = Arrays.asList(imagesUrlArray);

            List<SkuImagesEntity> skuImagesEntityList = IntStream.range(0, imagesUrlList.size())
                    .mapToObj(i -> {
                        SkuImagesEntity skuImagesEntity = new SkuImagesEntity();

                        skuImagesEntity.setImgUrl(imagesUrlList.get(i));

                        skuImagesEntity.setImgSort(i);

                        skuImagesEntity.setSkuId(skuId);

                        // 设置 默认图[0 - 不是默认图，1 - 是默认图]
                        if (imagesUrlList.get(i).equals(defaultImgUrl)) {
                            skuImagesEntity.setDefaultImg(1);
                        } else {
                            skuImagesEntity.setDefaultImg(0);
                        }

                        return skuImagesEntity;
                    }).collect(Collectors.toList());

            skuImagesService.saveBatch(skuImagesEntityList);
        }

    }


}